/*
 * PackageManagerGUIView.java
 */

package org.expath.pkg.exist;

import java.awt.FileDialog;
import org.jdesktop.application.Action;
import org.jdesktop.application.ResourceMap;
import org.jdesktop.application.SingleFrameApplication;
import org.jdesktop.application.FrameView;
import org.jdesktop.application.TaskMonitor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.util.List;
import javax.swing.Timer;
import javax.swing.Icon;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.table.AbstractTableModel;

/**
 * The application's main frame.
 */
public class PackageManagerGUIView
        extends FrameView
{
    public PackageManagerGUIView(SingleFrameApplication app)
    {
        super(app);
        myApp = app;

        initComponents();

        // status bar initialization - message timeout, idle icon and busy animation, etc
        ResourceMap resourceMap = getResourceMap();
        int messageTimeout = resourceMap.getInteger("StatusBar.messageTimeout");
        messageTimer = new Timer(messageTimeout, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                statusMessageLabel.setText("");
            }
        });
        messageTimer.setRepeats(false);
        int busyAnimationRate = resourceMap.getInteger("StatusBar.busyAnimationRate");
        for (int i = 0; i < busyIcons.length; i++) {
            busyIcons[i] = resourceMap.getIcon("StatusBar.busyIcons[" + i + "]");
        }
        busyIconTimer = new Timer(busyAnimationRate, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                busyIconIndex = (busyIconIndex + 1) % busyIcons.length;
                statusAnimationLabel.setIcon(busyIcons[busyIconIndex]);
            }
        });
        idleIcon = resourceMap.getIcon("StatusBar.idleIcon");
        statusAnimationLabel.setIcon(idleIcon);
        progressBar.setVisible(false);

        // connecting action tasks to status bar via TaskMonitor
        TaskMonitor taskMonitor = new TaskMonitor(getApplication().getContext());
        taskMonitor.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
            public void propertyChange(java.beans.PropertyChangeEvent evt) {
                String propertyName = evt.getPropertyName();
                if ("started".equals(propertyName)) {
                    if (!busyIconTimer.isRunning()) {
                        statusAnimationLabel.setIcon(busyIcons[0]);
                        busyIconIndex = 0;
                        busyIconTimer.start();
                    }
                    progressBar.setVisible(true);
                    progressBar.setIndeterminate(true);
                } else if ("done".equals(propertyName)) {
                    busyIconTimer.stop();
                    statusAnimationLabel.setIcon(idleIcon);
                    progressBar.setVisible(false);
                    progressBar.setValue(0);
                } else if ("message".equals(propertyName)) {
                    String text = (String)(evt.getNewValue());
                    statusMessageLabel.setText((text == null) ? "" : text);
                    messageTimer.restart();
                } else if ("progress".equals(propertyName)) {
                    int value = (Integer)(evt.getNewValue());
                    progressBar.setVisible(true);
                    progressBar.setIndeterminate(false);
                    progressBar.setValue(value);
                }
            }
        });

        // -fgeorges: additional code

        try {
            // the eXist instance from the env variable
            String home = System.getenv("EXIST_HOME");
            if ( home == null ) {
                warning("$EXIST_HOME is not defined.  You can open an exist instance in the menu.");
            }
            else {
                setHome(new File(home));
            }
        }
        catch ( FileNotFoundException ex ) {
            ex.printStackTrace();
            fatalError("Error: " + ex);
        }
        catch ( PackageException ex ) {
            ex.printStackTrace();
            fatalError("Error: " + ex);
        }
    }

    private void setHome(File home)
            throws FileNotFoundException
                 , PackageException
    {
        boolean is_first_time = myHome == null;
        myHomeValueLbl.setText(home.getAbsolutePath());
        // JFrame frame = PackageManagerGUI.getApplication().getMainFrame();
        JFrame frame = getFrame();
        myHome = new ExistInstance(home, new GraphicUserInteraction(frame));
        if ( is_first_time ) {
            // set the table and the table model (which depends on myRepo)
            myModulesTblModel = new ModuleTableModel();
            myModulesTbl.setModel(myModulesTblModel);
            // configure the selection model on the table
            ListSelectionModel select_model = myModulesTbl.getSelectionModel();
            select_model.addListSelectionListener(new SelectionListener());
            select_model.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            // enable install and delete menu actions
            myMenuItemInstall.setEnabled(true);
            myMenuItemDelete.setEnabled(true);
        }
        refreshTable();
    }

    @Action
    public void openInstance()
    {
        try {
            File dir = selectFile("Open instance", false);
            if ( dir == null ) {
                System.err.println("Package choose dialog canceled by user");
            }
            else if ( ! dir.exists() ) {
                warning("The directory does not exist: " + dir);
            }
            else if ( ! dir.isDirectory() ) {
                warning("The file is not a directory: " + dir);
            }
            else {
                setHome(dir);
            }
        }
        catch ( FileNotFoundException ex ) {
            ex.printStackTrace();
            warning(ex.getMessage());
        }
        catch ( PackageException ex ) {
            ex.printStackTrace();
            warning(ex.getMessage());
        }
    }

    @Action
    public void installPackage()
    {
        try {
            File pkg = selectFile("Install package", true);
            if ( pkg == null ) {
                System.err.println("Package choose dialog canceled by user");
            }
            else {
                boolean force = false;
                myHome.install(pkg, force);
            }
        }
        catch ( PackageException ex ) {
            ex.printStackTrace();
            warning(ex.getMessage());
        }
        refreshTable();
    }

    @Action
    public void deletePackage()
    {
        try {
            int idx = myModulesTbl.getSelectedRow();
            if ( idx < 0 ) {
                warning("No package selected.");
            }
            else {
                Module m = myModules.get(idx);
                myHome.delete(m);
            }
        }
        catch ( PackageException ex ) {
            ex.printStackTrace();
            warning(ex.getMessage());
        }
        refreshTable();
    }

    @Action
    public void showAboutBox()
    {
        if (aboutBox == null) {
            // JFrame mainFrame = PackageManagerGUI.getApplication().getMainFrame();
            JFrame mainFrame = getFrame();
            aboutBox = new PackageManagerGUIAboutBox(mainFrame);
            aboutBox.setLocationRelativeTo(mainFrame);
        }
        PackageManagerGUI.getApplication().show(aboutBox);
    }

    /**
     * Encapsulate myRepo.getModules(), without throwing any exception.
     *
     * This is for classes implementing Swing interfaces, which do not define
     * exception that can be throwed from the methods.  So this method catch
     * any exception from Repository.getModules(), and die with a message.
     *
     * TODO: Replace fatalError() with resetting the eXist instance to nothing
     * (like when starting and that EXIST_HOME is not set...)
     */
    private List<Module> getModulesWithoutException()
    {
        try {
            return myHome.getModules();
        }
        catch ( PackageException ex ) {
            ex.printStackTrace();
            fatalError(ex.getMessage());
            // cannot happen, thanks to fatalError(), but compiler needs it
            throw new RuntimeException(ex);
        }
    }

    private File selectFile(String title, boolean xar)
    {
        // Swing version, using JFileChooser, looks terrible on Mac OS X.  But
        // it seems that with the AWT version, using FileDialog, which looks
        // good on Mac OS X, it is not possible to select directories...
        // So if under Mac and selecting a file, use AWT, if not use Swing.
        if ( xar && isUnderMac ) {
            return selectFileAwt(title);
        }
        else {
            return selectFileSwing(title, xar);
        }
    }

    private File selectFileAwt(String title)
    {
        FileDialog dialog = new FileDialog(getFrame(), title, FileDialog.LOAD);
        dialog.setFilenameFilter(new ExtensionFilter("xar"));
        dialog.setVisible(true);
        String f = dialog.getFile();
        if ( f == null ) {
            return null;
        }
        else {
            return new File(dialog.getDirectory(), f);
        }
    }

    private File selectFileSwing(String title, boolean xar)
    {
        JFileChooser chooser = new JFileChooser();
        chooser.setMultiSelectionEnabled(false);
        if ( xar ) {
            chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            FileFilter filter = new FileNameExtensionFilter("X* package", "xar");
            chooser.addChoosableFileFilter(filter);
        }
        else {
            chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        }
        JFrame frame = getFrame();
        int res = chooser.showDialog(frame, title);
        if ( res == JFileChooser.APPROVE_OPTION ) {
            return chooser.getSelectedFile();
        }
        else {
            return null;
        }
    }

    private void refreshTable()
    {
        myModules = getModulesWithoutException();
        myModulesTbl.tableChanged(new TableModelEvent(myModulesTblModel));
        myModulesTbl.repaint();
    }

    private void warning(String msg)
    {
        //JFrame frame = PackageManagerGUI.getApplication().getMainFrame();
        JFrame frame = getFrame();
        int type = JOptionPane.WARNING_MESSAGE;
        JOptionPane.showMessageDialog(frame, "Error: " + msg, "Error", type);
    }

    private void fatalError(String msg)
    {
        //JFrame frame = PackageManagerGUI.getApplication().getMainFrame();
        JFrame frame = getFrame();
        int type = JOptionPane.ERROR_MESSAGE;
        JOptionPane.showMessageDialog(frame, "Error: " + msg, "Error", type);
        myApp.exit();
    }

    private ExistInstance myHome;
    private List<Module> myModules = null;
    private SingleFrameApplication myApp;
    private ModuleTableModel myModulesTblModel;
    private static final boolean isUnderMac = "Mac OS X".equals(System.getProperty("os.name"));

    /**
     * Depends on myHome.
     */
    private class ModuleTableModel
            extends AbstractTableModel
    {
        @Override
        public String getColumnName(int idx) {
            String[] names = {"Jar", "Name"};
            return names[idx];
        }
        @Override
        public int getRowCount() {
            if ( myModules == null ) {
                return 0;
            }
            return myModules.size();
        }
        @Override
        public int getColumnCount() {
            return 2;
        }
        @Override
        public Object getValueAt(int row, int col) {
            if ( myModules == null ) {
                return null;
            }
            Module m = myModules.get(row);
            return col == 0 ? m.getJar().getName() : m.getTitle();
        }
    }

    /**
     * Depends on myHome.
     */
    private class SelectionListener
            implements ListSelectionListener
    {
        @Override
        public void valueChanged(ListSelectionEvent e) {
            if ( ! e.getValueIsAdjusting() ) {
                ListSelectionModel sel = (ListSelectionModel) e.getSource();
                int idx = sel.getAnchorSelectionIndex();
                if ( idx < 0 ) {
                    myNameValueLbl.setVisible(false);
                    myVersionValueLbl.setVisible(false);
                    myJarValueLbl.setVisible(false);
                }
                else {
                    Module m = myModules.get(idx);
                    myNameValueLbl.setText(m.getTitle());
                    myNameValueLbl.setVisible(true);
                    myVersionValueLbl.setText(m.getVersion());
                    myVersionValueLbl.setVisible(true);
                    myJarValueLbl.setText(m.getJar().getAbsolutePath());
                    myJarValueLbl.setVisible(true);
                }
            }
        }
    }

    private static class ExtensionFilter
            implements FilenameFilter
    {
        public ExtensionFilter(String ext) {
            myExt = "." + ext;
        }
        @Override
        public boolean accept(File dir, String name) {
            return name.endsWith(myExt);
        }
        private String myExt;
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        myMainPanel = new javax.swing.JPanel();
        myHomeLbl = new javax.swing.JLabel();
        myModulesLbl = new javax.swing.JLabel();
        myModulesTableScrl = new javax.swing.JScrollPane();
        myModulesTbl = new javax.swing.JTable();
        myJarLbl = new javax.swing.JLabel();
        myVersionLbl = new javax.swing.JLabel();
        myNameLbl = new javax.swing.JLabel();
        myVersionValueLbl = new javax.swing.JLabel();
        myJarValueLbl = new javax.swing.JLabel();
        myNameValueLbl = new javax.swing.JLabel();
        myHomeValueLbl = new javax.swing.JLabel();
        myMenuBar = new javax.swing.JMenuBar();
        javax.swing.JMenu myMenuFile = new javax.swing.JMenu();
        myMenuItemOpen = new javax.swing.JMenuItem();
        myMenuItemInstall = new javax.swing.JMenuItem();
        myMenuItemDelete = new javax.swing.JMenuItem();
        javax.swing.JMenuItem myMenuItemQuit = new javax.swing.JMenuItem();
        javax.swing.JMenu myMenuHelp = new javax.swing.JMenu();
        javax.swing.JMenuItem aboutMenuItem = new javax.swing.JMenuItem();
        myStatusPanel = new javax.swing.JPanel();
        javax.swing.JSeparator statusPanelSeparator = new javax.swing.JSeparator();
        statusMessageLabel = new javax.swing.JLabel();
        statusAnimationLabel = new javax.swing.JLabel();
        progressBar = new javax.swing.JProgressBar();

        myMainPanel.setName("myMainPanel"); // NOI18N

        org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(org.expath.pkg.exist.PackageManagerGUI.class).getContext().getResourceMap(PackageManagerGUIView.class);
        myHomeLbl.setText(resourceMap.getString("myHomeLbl.text")); // NOI18N
        myHomeLbl.setName("myHomeLbl"); // NOI18N

        myModulesLbl.setText(resourceMap.getString("myModulesLbl.text")); // NOI18N
        myModulesLbl.setName("myModulesLbl"); // NOI18N

        myModulesTableScrl.setName("myModulesTableScrl"); // NOI18N

        myModulesTbl.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {},
                {},
                {},
                {}
            },
            new String [] {

            }
        ));
        myModulesTbl.setName("myModulesTbl"); // NOI18N
        myModulesTableScrl.setViewportView(myModulesTbl);

        myJarLbl.setText(resourceMap.getString("myJarLbl.text")); // NOI18N
        myJarLbl.setName("myJarLbl"); // NOI18N

        myVersionLbl.setText(resourceMap.getString("myVersionLbl.text")); // NOI18N
        myVersionLbl.setName("myVersionLbl"); // NOI18N

        myNameLbl.setText(resourceMap.getString("myNameLbl.text")); // NOI18N
        myNameLbl.setName("myNameLbl"); // NOI18N

        myVersionValueLbl.setText(resourceMap.getString("myVersionValueLbl.text")); // NOI18N
        myVersionValueLbl.setName("myVersionValueLbl"); // NOI18N

        myJarValueLbl.setText(resourceMap.getString("myJarValueLbl.text")); // NOI18N
        myJarValueLbl.setName("myJarValueLbl"); // NOI18N

        myNameValueLbl.setText(resourceMap.getString("myNameValueLbl.text")); // NOI18N
        myNameValueLbl.setName("myNameValueLbl"); // NOI18N

        myHomeValueLbl.setText(resourceMap.getString("myHomeValueLbl.text")); // NOI18N
        myHomeValueLbl.setName("myHomeValueLbl"); // NOI18N

        javax.swing.GroupLayout myMainPanelLayout = new javax.swing.GroupLayout(myMainPanel);
        myMainPanel.setLayout(myMainPanelLayout);
        myMainPanelLayout.setHorizontalGroup(
            myMainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(myMainPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(myMainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(myModulesTableScrl, javax.swing.GroupLayout.DEFAULT_SIZE, 523, Short.MAX_VALUE)
                    .addGroup(myMainPanelLayout.createSequentialGroup()
                        .addComponent(myHomeLbl)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(myHomeValueLbl, javax.swing.GroupLayout.DEFAULT_SIZE, 458, Short.MAX_VALUE))
                    .addComponent(myModulesLbl)
                    .addGroup(myMainPanelLayout.createSequentialGroup()
                        .addGroup(myMainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(myVersionLbl)
                            .addComponent(myJarLbl)
                            .addComponent(myNameLbl))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(myMainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(myJarValueLbl, javax.swing.GroupLayout.DEFAULT_SIZE, 464, Short.MAX_VALUE)
                            .addComponent(myVersionValueLbl, javax.swing.GroupLayout.DEFAULT_SIZE, 464, Short.MAX_VALUE)
                            .addComponent(myNameValueLbl, javax.swing.GroupLayout.DEFAULT_SIZE, 464, Short.MAX_VALUE))))
                .addContainerGap())
        );
        myMainPanelLayout.setVerticalGroup(
            myMainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(myMainPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(myMainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(myHomeLbl)
                    .addComponent(myHomeValueLbl))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(myModulesLbl)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(myModulesTableScrl, javax.swing.GroupLayout.DEFAULT_SIZE, 206, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(myMainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(myNameLbl)
                    .addComponent(myNameValueLbl))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(myMainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(myVersionLbl)
                    .addComponent(myVersionValueLbl))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(myMainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(myJarLbl)
                    .addComponent(myJarValueLbl))
                .addContainerGap())
        );

        myMenuBar.setName("myMenuBar"); // NOI18N

        myMenuFile.setText(resourceMap.getString("myMenuFile.text")); // NOI18N
        myMenuFile.setName("myMenuFile"); // NOI18N

        javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(org.expath.pkg.exist.PackageManagerGUI.class).getContext().getActionMap(PackageManagerGUIView.class, this);
        myMenuItemOpen.setAction(actionMap.get("openInstance")); // NOI18N
        myMenuItemOpen.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, java.awt.event.InputEvent.CTRL_MASK));
        myMenuItemOpen.setText(resourceMap.getString("myMenuItemOpen.text")); // NOI18N
        myMenuItemOpen.setName("myMenuItemOpen"); // NOI18N
        myMenuFile.add(myMenuItemOpen);

        myMenuItemInstall.setAction(actionMap.get("installPackage")); // NOI18N
        myMenuItemInstall.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_I, java.awt.event.InputEvent.CTRL_MASK));
        myMenuItemInstall.setText(resourceMap.getString("myMenuItemInstall.text")); // NOI18N
        myMenuItemInstall.setName("myMenuItemInstall"); // NOI18N
        myMenuFile.add(myMenuItemInstall);

        myMenuItemDelete.setAction(actionMap.get("deletePackage")); // NOI18N
        myMenuItemDelete.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_D, java.awt.event.InputEvent.CTRL_MASK));
        myMenuItemDelete.setText(resourceMap.getString("myMenuItemDelete.text")); // NOI18N
        myMenuItemDelete.setName("myMenuItemDelete"); // NOI18N
        myMenuFile.add(myMenuItemDelete);

        myMenuItemQuit.setAction(actionMap.get("quit")); // NOI18N
        myMenuItemQuit.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Q, java.awt.event.InputEvent.CTRL_MASK));
        myMenuItemQuit.setName("myMenuItemQuit"); // NOI18N
        myMenuFile.add(myMenuItemQuit);

        myMenuBar.add(myMenuFile);

        myMenuHelp.setText(resourceMap.getString("myMenuHelp.text")); // NOI18N
        myMenuHelp.setName("myMenuHelp"); // NOI18N

        aboutMenuItem.setAction(actionMap.get("showAboutBox")); // NOI18N
        aboutMenuItem.setName("aboutMenuItem"); // NOI18N
        myMenuHelp.add(aboutMenuItem);

        myMenuBar.add(myMenuHelp);

        myStatusPanel.setName("myStatusPanel"); // NOI18N

        statusPanelSeparator.setName("statusPanelSeparator"); // NOI18N

        statusMessageLabel.setName("statusMessageLabel"); // NOI18N

        statusAnimationLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
        statusAnimationLabel.setName("statusAnimationLabel"); // NOI18N

        progressBar.setName("progressBar"); // NOI18N

        javax.swing.GroupLayout myStatusPanelLayout = new javax.swing.GroupLayout(myStatusPanel);
        myStatusPanel.setLayout(myStatusPanelLayout);
        myStatusPanelLayout.setHorizontalGroup(
            myStatusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(statusPanelSeparator, javax.swing.GroupLayout.DEFAULT_SIZE, 563, Short.MAX_VALUE)
            .addGroup(myStatusPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(statusMessageLabel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 367, Short.MAX_VALUE)
                .addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(statusAnimationLabel)
                .addContainerGap())
        );
        myStatusPanelLayout.setVerticalGroup(
            myStatusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(myStatusPanelLayout.createSequentialGroup()
                .addComponent(statusPanelSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 2, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addGroup(myStatusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(statusMessageLabel)
                    .addComponent(statusAnimationLabel)
                    .addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(3, 3, 3))
        );

        setComponent(myMainPanel);
        setMenuBar(myMenuBar);
        setStatusBar(myStatusPanel);
    }// </editor-fold>//GEN-END:initComponents

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel myHomeLbl;
    private javax.swing.JLabel myHomeValueLbl;
    private javax.swing.JLabel myJarLbl;
    private javax.swing.JLabel myJarValueLbl;
    private javax.swing.JPanel myMainPanel;
    private javax.swing.JMenuBar myMenuBar;
    private javax.swing.JMenuItem myMenuItemDelete;
    private javax.swing.JMenuItem myMenuItemInstall;
    private javax.swing.JMenuItem myMenuItemOpen;
    private javax.swing.JLabel myModulesLbl;
    private javax.swing.JScrollPane myModulesTableScrl;
    private javax.swing.JTable myModulesTbl;
    private javax.swing.JLabel myNameLbl;
    private javax.swing.JLabel myNameValueLbl;
    private javax.swing.JPanel myStatusPanel;
    private javax.swing.JLabel myVersionLbl;
    private javax.swing.JLabel myVersionValueLbl;
    private javax.swing.JProgressBar progressBar;
    private javax.swing.JLabel statusAnimationLabel;
    private javax.swing.JLabel statusMessageLabel;
    // End of variables declaration//GEN-END:variables

    private final Timer messageTimer;
    private final Timer busyIconTimer;
    private final Icon idleIcon;
    private final Icon[] busyIcons = new Icon[15];
    private int busyIconIndex = 0;

    private JDialog aboutBox;
}
